---
title: 工厂模式
description: 当创建对象的逻辑比较复杂的时候，就需要考虑使用工厂模式了，封装对象的创建过程，将对象的创建和使用相分离。其次可以降低代码的重复。
---
一般情况下，工厂模式分为三种更加细分的类型：简单工厂、工厂方法和抽象方法。

当创建对象的逻辑比较复杂的时候，就需要考虑使用工厂模式了，封装对象的创建过程，将对象的创建和使用相分离。
## 简单工厂

在下面的这段代码中，我们要根据配置文件的后缀（json、xml、yaml、properties），选择不同的解析器（JsonRuleConfigParser,XmlRuleConfigParser...）,将存储在文件中的配置解析成内存对象 RuleConfig。

```java
/**
 * @author 王开琦
 */
public class RuleConfigSource {
    public RuleConfig load(String ruleConfigFilePath) {
        String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
        IRuleConfigParser parser = null;
        switch (ruleConfigFilePath){
            case "json" -> parser=new JsonRuleConfigParser();
            case "xml" -> parser=new XmlRuleConfigParser();
            case "yaml" -> parser=new YamlRuleConfigParser();
            case "properties" -> parser=new PropertiesRuleConfigParser();
            default -> throw new InvalidRuleConfigException("Rule config file format is not supported"+ruleConfigFilePath);
        }
        String configText ="";
        //从 ruleConfigFilePath 文件中读取配置文本到 configText 中
        RuleConfig ruleConfig = parser.parse(configText);
        return ruleConfig;
    }

    private String getFileExtension(String filePath) {
        //...解析文件名获取扩展名，比如 rule.json,返回json
        return "json";
    }
}
```
为了让代码逻辑更加清晰，可读性更好，我们要善于将功能独立的代码块封装成函数。
```java title='将代码中设计 parser 创建的部分逻辑剥离出来，抽象成 createParser 方法。'
/**
 * @author 王开琦
 */
public class RuleConfigSource {
    public RuleConfig load(String ruleConfigFilePath) {
        String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
        IRuleConfigParser parser =  createParser(ruleConfigFilePath);
        String configText ="";
        //从 ruleConfigFilePath 文件中读取配置文本到 configText 中
        RuleConfig ruleConfig = parser.parse(configText);
        return ruleConfig;
    }

    private static IRuleConfigParser createParser(String ruleConfigFilePath) throws InvalidRuleConfigException {
        IRuleConfigParser parser;
        switch (ruleConfigFilePath){
            case "json" -> parser=new JsonRuleConfigParser();
            case "xml" -> parser=new XmlRuleConfigParser();
            case "yaml" -> parser=new YamlRuleConfigParser();
            case "properties" -> parser=new PropertiesRuleConfigParser();
            default -> throw new InvalidRuleConfigException("Rule config file format is not supported"+ ruleConfigFilePath);
        }
        return parser;
    }

    private String getFileExtension(String filePath) {
        //...解析文件名获取扩展名，比如 rule.json,返回json
        return "json";
    }
}
```
为了让类的职责更加单一、代码更加清晰，还可以进一步将 createParser 函数剥离到一个独立的类中，让这个类只负责创建。这个类就是简单工厂模式类。
```java title='简单工厂类'
package pub.wkq.create.factory;

/**
 * @author 王开琦
 */
public class RuleConfigParserFactory {
    public static IRuleConfigParser createParser(String ruleConfigFilePath) throws InvalidRuleConfigException {
        IRuleConfigParser parser;
        switch (ruleConfigFilePath){
            case "json" -> parser=new JsonRuleConfigParser();
            case "xml" -> parser=new XmlRuleConfigParser();
            case "yaml" -> parser=new YamlRuleConfigParser();
            case "properties" -> parser=new PropertiesRuleConfigParser();
            default -> throw new InvalidRuleConfigException("Rule config file format is not supported"+ ruleConfigFilePath);
        }
        return parser;
    }
}

package pub.wkq.create.factory;

/**
 * @author 王开琦
 */
public class RuleConfigSource {
    public RuleConfig load(String ruleConfigFilePath) {
        String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
        IRuleConfigParser parser =  RuleConfigParserFactory.createParser(ruleConfigFilePath);
        String configText ="";
        //从 ruleConfigFilePath 文件中读取配置文本到 configText 中
        RuleConfig ruleConfig = parser.parse(configText);
        return ruleConfig;
    }
    private String getFileExtension(String filePath) {
        //...解析文件名获取扩展名，比如 rule.json,返回json
        return "json";
    }
}
```
大部分工厂类都是以 Factory 这个单词结尾的，并且创建对象的方法一般以 create 开头，但也有 getInstance()、createInstance()、newInstance() 这种命名。

在上面的代码实现中，每次调用 RuleConfigParserFactory 的 createParser() 的时候，都要创建一个新的 parser。实际上，如果 parser 可用复用，为了节省内存和对象创建的时间，我们可以将 parser 实现创建好缓存起来。当调用 createParser() 函数的时候，我们可以从缓存中取出 parser 对象直接使用。

这点有点类似工厂模式和单例模式的结合，所以下面也可以叫做工厂模式的第二种实现方法：

```java title='工厂方法的第二种实现方法'

/**
 * @author 王开琦
 */
public class RuleConfigParserFactory {
    private static final Map<String, RuleConfigParser> cachedParsers = new HashMap<>();
    static {
        cachedParsers.put("json",new JsonRuleConfigParser());
        cachedParsers.put("xml",new XmlRuleConfigParser());
        cachedParsers.put("yaml",new YamlRuleConfigParser());
        cachedParsers.put("properties",new PropertiesRuleConfigParser());
    }
    public static IRuleConfigParser createParser(String configFormat)  {
        if (configFormat==null||configFormat.isBlank()){
            return null;
        }
        IRuleConfigParser parser=cachedParsers.get(configFormat.toLowerCase());
        return parser;
    }
}
```
对于上面两种 简单工厂模式的实现，如果我们要添加新的 parser ，就势必要改动 RuleConfigParserFactory 的代码。违反开闭原则。
除此之外，在RuleConfigParserFactory 的第一种代码实现中，有一组分支判断逻辑，也可以用多态或其它设计模式来替代，但也不是没有任何缺点的，它虽然提高了代码的扩展性，更加符合开闭原则，但也增加了类的个数，牺牲了代码的可读性。

:::tip
在简单工厂模式的代码实现中，有多处 if 分支判断逻辑，违背开闭原则，但权衡扩展性和可读性，这样的代码实现在大多数情况下是没有问题的。
:::

## 工厂方法
如果要将 if 分支逻辑去掉，比较经典的处理方法就是利用多态。
